﻿using System;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace UTSF.Media.ImageManipulation
{
    /// <summary>
    /// The manipulator class contains methods for editing an image in Silverlight.
    /// These edits are destructive in that they directly update the pixels in the image
    /// and return the updated image as a WriteableBitmap.
    /// </summary>
    public class Manipulator
    {
        /// <summary>
        /// Method which hard resizes an image to the specified width and height.
        /// It does not maintain aspect ratio.
        /// </summary>
        /// <param name="sourceImage"></param>
        /// <param name="targetWidth"></param>
        /// <param name="targetHeight"></param>
        /// <returns>WritableBitmap of the resized image</returns>
        public static WriteableBitmap ResizeImage(WriteableBitmap sourceImage, int targetWidth, int targetHeight)
        {
            WriteableBitmap wb = new WriteableBitmap(targetWidth, targetHeight);

            //Draw to the Writeable Bitmap
            Image tempImage2 = new Image();
            tempImage2.Width = targetWidth;
            tempImage2.Height = targetHeight;
            tempImage2.Source = sourceImage;

            wb.Render(tempImage2, null);
            wb.Invalidate();

            return wb;
        }

        /// <summary>
        /// Method which crops an image using the specified RectangleGeometry
        /// </summary>
        /// <param name="sourceImage"></param>
        /// <param name="cropArea"></param>
        /// <returns>WritableBitmap of the copped image</returns>
        public static WriteableBitmap CropImage(WriteableBitmap sourceImage, RectangleGeometry cropArea)
        {
            return CropImage(sourceImage, (int)cropArea.Bounds.X, (int)cropArea.Bounds.Y, (int)cropArea.Bounds.Width, (int)cropArea.Bounds.Height);
        }

        /// <summary>
        /// Method which crops an image based on the supplied offset and size
        /// </summary>
        /// <param name="sourceImage"></param>
        /// <param name="offsetX"></param>
        /// <param name="offsetY"></param>
        /// <param name="targetWidth"></param>
        /// <param name="targetHeight"></param>
        /// <returns>WritableBitmap of the cropped image</returns>
        public static WriteableBitmap CropImage(WriteableBitmap sourceImage, int offsetX, int offsetY, int targetWidth, int targetHeight)
        {
            //Create a bitmap of the cropped size
            WriteableBitmap wb = new WriteableBitmap(targetWidth, targetHeight);

            //offset cop from top left of image
            TranslateTransform t = new TranslateTransform();
            t.X = -offsetX;
            t.Y = -offsetY;

            //Draw to the Writeable Bitmap
            Image tempImage2 = new Image();
            tempImage2.Source = sourceImage;

            wb.Render(tempImage2, t);
            wb.Invalidate();

            return wb;
        }

        /// <summary>
        /// Method wich rotates an image clockwise by 90 degrees.
        /// </summary>
        /// <param name="sourceImage"></param>
        /// <returns>WritableBitmap of the rotated image</returns>
        public static WriteableBitmap RotateImage90Degrees(WriteableBitmap sourceImage)
        {
            //Create a bitmap of the image size
            int width = sourceImage.PixelWidth;
            int height = sourceImage.PixelHeight;
            int destWidth = width;
            int destHeight = height;
            bool landscape = true;

            if (width > height)
            {
                destHeight = width;
                destWidth = height;
            }
            else if (height > width)
            {
                landscape = false;
                destWidth = height;
                destHeight = width;
            }

            WriteableBitmap wb = new WriteableBitmap(destWidth, destHeight);

            RotateTransform rt = new RotateTransform();
            rt.Angle = 90;
            if (landscape)
                rt.CenterX = rt.CenterY = Math.Min(width / 2, height / 2);
            else
                rt.CenterX = rt.CenterY = Math.Max(height / 2, width / 2);

            //Draw to the Writeable Bitmap
            Image tempImage2 = new Image();
            tempImage2.Width = width;
            tempImage2.Height = height;
            tempImage2.Source = sourceImage;

            wb.Render(tempImage2, rt);
            wb.Invalidate();

            return wb;
        }

        /// <summary>
        /// Method which flips an image horizontally. It uses the MatrixTransform method to perforn
        /// the flip, it isjust a proxy for a predefined matrix transform.
        /// </summary>
        /// <param name="rawImage"></param>
        /// <returns>WriteableBitmap of the flippped image</returns>
        public static WriteableBitmap FlipHorizontal(WriteableBitmap rawImage)
        {
            Matrix matrix = new Matrix(1, 0, 0, -1, 0, rawImage.PixelHeight);
            return MatrixTransform(rawImage, matrix);
        }

        /// <summary>
        /// Method which flips an image vertically. It uses the MatrixTransform method to perforn
        /// the flip, it isjust a proxy for a predefined matrix transform.
        /// </summary>
        /// <param name="rawImage"></param>
        /// <returns>WriteableBitmap of the flippped image</returns>
        public static WriteableBitmap FlipVertical(WriteableBitmap rawImage)
        {
            Matrix matrix = new Matrix(-1, 0, 0, 1, rawImage.PixelWidth, 0);
            return MatrixTransform(rawImage, matrix);
        }

        /// <summary>
        /// Method which applies the specified Matrix transform to the image, it is used by other
        /// methods in such as FlipVertical but can also be used to perform complex transformations
        /// such as skewing. 
        /// </summary>
        /// <param name="sourceImage"></param>
        /// <param name="matrix"></param>
        /// <returns>WritableBitmap of the transformed image</returns>
        public static WriteableBitmap MatrixTransform(WriteableBitmap sourceImage, Matrix matrix)
        {
            WriteableBitmap wb = new WriteableBitmap(sourceImage.PixelWidth, sourceImage.PixelHeight);

            MatrixTransform mt = new MatrixTransform();
            mt.Matrix = matrix;

            //Draw to the Writeable Bitmap
            Image tempImage2 = new Image();
            tempImage2.Source = sourceImage;
            wb.Render(tempImage2, mt);
            wb.Invalidate();

            return wb;
        }
    }
}
